package com.qfmy.app.service.impl.novel;

import cn.hutool.json.JSONUtil;
import com.qfmy.app.lucene.LuceneUtils;
import com.qfmy.app.mapper.novel.BookMapper;
import com.qfmy.app.mapper.novel.ReadRecordMapper;
import com.qfmy.app.mapper.user.UserMapper;
import com.qfmy.app.service.novel.BookService;
import com.qfmy.app.vo.novel.BookVo;
import com.qfmy.common.exception.BaseException;
import com.qfmy.common.login.LoginUser;
import com.qfmy.common.login.LoginUserHolder;
import com.qfmy.common.result.ResultCodeEnum;
import com.qfmy.model.entity.novel.Book;
import com.qfmy.model.entity.novel.ReadRecord;
import com.qfmy.model.entity.query.CategoryQuery;
import com.qfmy.model.entity.user.Users;
import com.qfmy.model.enums.novel.BookStatus;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 清风明月
 * @Package com.qfmy.app.service.impl.novel
 * @date 2025/4/14
 * @description 小说的服务类
 */
@Slf4j
@Service
@SuppressWarnings("all")
public class BookServiceImpl implements BookService {

    /**
     * 注入小说mapper
     */
    @Autowired
    private BookMapper bookMapper;
    /**
     * 注入redisTemplate
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 注入用户mapper
     * @return
     */
    @Autowired
    private UserMapper userMapper;

    /**
     * 注入用户阅读记录mapper
     * @return
     */
    @Autowired
    private ReadRecordMapper readRecordMapper;


    @Override
    public List<BookVo> getTopThree() {
        // 获取key
        String key = "novel:topThree";
        // 使用BoundValueOperations简化操作
        BoundValueOperations<String, List<BookVo>> boundValueOps = redisTemplate.boundValueOps(key);

        // 从缓存里面获取数据
        List<BookVo> bookVOS = boundValueOps.get();

        // 判断是否存在
        if (bookVOS != null) {
            return bookVOS;
        }

        // 获取三个热门小说
        List<Book> books = bookMapper.selectTopThree(3);

        // 对数据进行拷贝
        List<BookVo> bk = CopyBook(books);

        // 异步缓存数据，并设置过期时间
        CompletableFuture.runAsync(() -> {
            boundValueOps.set(bk, 1, TimeUnit.HOURS); // 设置1小时过期
        });

        return bk;
    }

    /**
     * 获取推荐小说
     * @return 推荐小说列表
     */
    @Override
    public List<BookVo> getRecommend() {
        // 获取用户信息
        Users user = getUserInfo();

        // 如果用户信息为空，直接返回默认推荐
        if (user == null) {
            return getDefaultRecommend();
        }

        // 获取用户最近的阅读记录（最多10条）
        List<ReadRecord> recentBooks = readRecordMapper.selectRecentReadBooks(user.getUserId(), 1, 10);

        // 如果用户没有阅读记录，返回默认推荐
        if (recentBooks.isEmpty()) {
            return getDefaultRecommend();
        }

            // 分析用户的阅读兴趣（按分类统计）
        Map<String, Integer> categoryMap = new HashMap<>();
        for (ReadRecord record : recentBooks) {
            String category = record.getBook().getType();
            categoryMap.compute(category, (k, v) -> v == null ? 1 : v + 1);
        }

        // 提取高频分类（最多5个）
        List<String> topCategories = categoryMap.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .limit(5)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

        // 获取用户已读图书ID列表（用于过滤已读图书）
        List<Integer> readBookIds = recentBooks.stream()
                .map(record -> record.getBook().getBookId())
                .distinct()
                .collect(Collectors.toList());

        // 根据高频分类查询推荐图书（排除已读图书）
        List<Book> recommendedBooks = bookMapper.selectRecommendationsByCategories(
                topCategories,
                readBookIds
        );

        // 如果推荐结果为空，返回默认推荐
        if (recommendedBooks.isEmpty()) {
            return getDefaultRecommend();
        }

        // 转换为VO并返回
        return CopyBook(recommendedBooks);
    }

    /**
     * 获取用户信息
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private Users getUserInfo() {
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        if (loginUser == null) {
            throw new BaseException(ResultCodeEnum.TOKEN_INVALID);
        }
        // 获取手机号
        String phone = loginUser.getPhone();
        // 从缓存里面获取用户信息
        Users user = JSONUtil.toBean( (String) redisTemplate.opsForValue().get(phone),Users.class);
        if (user != null) {
            return user;
        }
        // 从数据库里面获取用户信息
        user = userMapper.getUserByPhone(phone);
        log.info("用户{}",user);
        // 将用户信息存入缓存
        redisTemplate.opsForValue().set(phone, JSONUtil.toJsonStr(user), 1, TimeUnit.HOURS);
        return user;
    }

    /**
     * 获取默认推荐（热门图书）
     */
    private List<BookVo> getDefaultRecommend() {
        //获取热门小说
        List<Book> books = bookMapper.selectTopThree(10);
        // 将数据拷贝到VO对象中
        return CopyBook(books);
    }

    /**
     * 分页获取所有的小说（带缓存）
     * @param pageNum 当前页码
     * @param size 每页大小
     * @return 分页后的小说列表
     */
    @Override
    public List<BookVo> getAll(int pageNum, int size) {
        // 计算分页起始位置
        int start = calculateStart(pageNum, size);

        // 生成缓存键
        String cacheKey = generateCacheKey(pageNum, size);

        // 尝试从缓存中获取数据
        List<BookVo> cachedBookVOS = getFromCache(cacheKey);
        if (cachedBookVOS != null) {
            return cachedBookVOS;
        }

        // 从数据库中查询数据
        List<Book> books = queryBooksFromDatabase(start, size);

        // 将查询结果转换为VO对象
        List<BookVo> bookVOS = CopyBook(books);

        // 异步缓存数据
        cacheDataAsync(cacheKey, bookVOS);

        return bookVOS;
    }

    /**
     * 根据分类条件查询小说
     * @param categoryQuery
     * @return
     */
    @Override
    public List<BookVo> getByCategory(@NotNull CategoryQuery categoryQuery) {
        //计算分页条件
        int start = calculateStart(categoryQuery.getPageNum(), categoryQuery.getSize());
        //设置分页条件
        categoryQuery.setPageNum(start);
        Map<Integer, Function<CategoryQuery, List<Book>>> queryFunctions = new HashMap<>();
        queryFunctions.put(0, bookMapper::selectByCategory);
        queryFunctions.put(1, bookMapper::selectByReadCount);
        queryFunctions.put(2, bookMapper::selectByScore);
        queryFunctions.put(3, bookMapper::selectByUpdateTime);

        // 获取查询函数
        Function<CategoryQuery, List<Book>> queryFunction = queryFunctions.get(categoryQuery.getType());
        if (queryFunction == null) {
            throw new IllegalArgumentException("不支持的查询类型: " + categoryQuery.getType());
        }
        // 执行查询
        List<Book> books = queryFunction.apply(categoryQuery);
        // 数据转换
        return CopyBook(books);
    }

    /**
     * 根据ID获取小说
     * @param id
     * @return
     */
    @Override
    public BookVo getById(Long id) {
        //获取当前用户id
        LoginUser loginUser = LoginUserHolder.getLoginUser();
        Integer userId = loginUser.getUserId();
        //根据id查询小说
        Book book = bookMapper.selectById(id,userId);
        //对数据进行拷贝
        if (book != null) {
            BookVo bookVO = new BookVo();
            BeanUtils.copyProperties(book, bookVO);
            //根据根据状态获取状态的描述
            String descByCode = BookStatus.getDescByCode(book.getStatus());
            bookVO.setStatus(descByCode);
            return bookVO;
        }
        return null;
    }

    /**
     * 根据标签获取小说
     * @param tag
     * @param pageNum
     * @param size
     * @return
     */
    @Override
    public List<BookVo> getByTag(@NotNull String tag, int pageNum, int size) {
        //计算分页
        int start = getStart(pageNum, size);
        //使用Map+函数式接口进行映射
        Map<String,Function<Integer, List<BookVo>>> queryFunctions = new HashMap<>();
        queryFunctions.put("recommend",(offset)->CopyBook(bookMapper.selectRecommend(start,size)));
        queryFunctions.put("complete",(offset)->CopyBook(bookMapper.selectByComplete(start,size)));
        queryFunctions.put("new",(offset) ->CopyBook(bookMapper.selectByNew(start,size)));
        //根据标签获取查询方法执行查询
        List<BookVo> bookVoList = queryFunctions.getOrDefault(tag, (offset) -> new ArrayList<>()).apply(start);
        return bookVoList;
    }

    /**
     * 小说的搜索
     * @param bookQuery
     * @return
     */
    @Override
    public List<BookVo> search(String keyword) {
        try {
            return LuceneUtils.searchBooks(keyword);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取所有的小说
     */
    public void indexBooks() {
        try {
            List<Book> books = bookMapper.getAllBooks();
            LuceneUtils.createIndex(books);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新收索次数
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSearchCount(Long id) {
        bookMapper.updateSearchCount(id);
    }

    /**
     * 更新阅读次数
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateReadCount(Long id) {
        bookMapper.updateReadCount(id);
    }

    /**
     * 获取数据
     * @param pageNum
     * @param size
     * @return
     */
    @Override
    public List<BookVo> getHotSearch(int pageNum, int size) {
        //计算分页
        int start = getStart(pageNum, size);
        //从数据库中查询
        List<Book> books = bookMapper.selectHotSearch(start, size);
        if (books != null) {
            return CopyBook(books);
        }
        return List.of();
    }

    /**
     * 计算分页起始位置
     * @param pageNum
     * @param size
     * @return
     */
    private int getStart(int pageNum, int size) {
       pageNum = Math.max(pageNum, 1);
       size = Math.max(size, 10);
       return (pageNum - 1) * size;
    }


    /**
     * 生成缓存键
     * @param pageNum 当前页码
     * @param size 每页大小
     * @return 缓存键
     */
    @NotNull
    @Contract(pure = true)
    private String generateCacheKey(int pageNum, int size) {
       return String.format("novel:all:%d:%d", pageNum, size);
    }



    /**
     * 从缓存中获取数据
     * @param cacheKey 缓存键
     * @return 缓存中的数据（如果存在）
     */
    private List<BookVo> getFromCache(String cacheKey) {
        BoundValueOperations<String, List<BookVo>> boundValueOps = redisTemplate.boundValueOps(cacheKey);
        return boundValueOps.get();
    }

    /**
     * 从数据库中查询分页数据
     * @param start 起始位置
     * @param size 每页大小
     * @return 查询结果
     */
    private List<Book> queryBooksFromDatabase(int start, int size) {
        return bookMapper.selectPage(start, size);
    }

    /**
     * 异步缓存数据
     * @param cacheKey 缓存键
     * @param bookVOS 数据
     */
    private void cacheDataAsync(String cacheKey, List<BookVo> bookVOS) {
        CompletableFuture.runAsync(() -> {
            BoundValueOperations<String, List<BookVo>> boundValueOps = redisTemplate.boundValueOps(cacheKey);
            boundValueOps.set(bookVOS, 1, TimeUnit.HOURS); // 设置1小时过期
        });
    }

    /**
     * 计算分页起始位置
     * @param pageNum 当前页码
     * @param size 每页大小
     * @return 起始位置
     */
    private int calculateStart(int pageNum, int size) {
        // 设置默认值
        if (pageNum <= 0) {
            pageNum = 1;
        }
        if (size <= 0) {
            size = 10;
        }
        // 计算分页起始位置
        return (pageNum - 1) * size;
    }

    /**
     * 拷贝数据
     * @param books
     * @return
     */
    @Nullable
    @Contract(pure = true)
    private List<BookVo> CopyBook(@NotNull List<Book> books) {
        // 判断是否有数据
        if (books.isEmpty()) {
            return new ArrayList<>();
        }

        // 拷贝数据
        List<BookVo> bookVOS = new ArrayList<>();
        for (Book book : books) {
            try {
                // 创建BookVO对象
                BookVo bookVO = new BookVo();
                // 拷贝数据
                BeanUtils.copyProperties(book, bookVO);

                // 提前处理状态
                String status = BookStatus.getDescByCode(book.getStatus());
                bookVO.setStatus(status);

                // 添加到集合中
                bookVOS.add(bookVO);
            } catch (Exception e) {
                // 记录异常，避免单条数据异常影响整个流程
                log.error("数据拷贝异常", e);
            }
        }

        return bookVOS;
    }
}
